home *** CD-ROM | disk | FTP | other *** search
/ Power DOS 1996 July / Power DOS - July 1996.iso / sound / c_labs / devinfo / mpu401.exe / MIDIUTIL.ASM < prev    next >
Encoding:
Assembly Source File  |  1996-04-18  |  19.5 KB  |  624 lines

  1. ; --------------------------------------------------------------------------
  2. ; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  3. ; KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  4. ; IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  5. ; PURPOSE.
  6. ;
  7. ; You have a royalty-free right to use, modify, reproduce and
  8. ; distribute the Sample Files (and/or any modified version) in
  9. ; any way you find useful, provided that you agree that
  10. ; Creative has no warranty obligations or liability for any Sample Files.
  11. ;----------------------------------------------------------------------------
  12.  
  13. ; ---------------------------------------------------------------------------
  14. ; Program:  Sound Blaster 16 MIDI Utilities
  15. ; Filename: MIDIUTIL.ASM
  16. ; Author:   Scott E. Sindorf
  17. ; Language: Borland TASM
  18. ; Date:     30 Jun 93
  19. ;
  20. ; Copyright (c) 1993-1996 Creative Labs, Inc.
  21. ; ---------------------------------------------------------------------------
  22.  
  23. SBMIDI         equ   0
  24. MPU401         equ   1
  25.  
  26. ; MPU-401 definitions
  27.  
  28. MPU401_RESET   equ   0ffh
  29. MPU401_UART    equ   03fh
  30. MPU401_CMDOK   equ   0feh
  31. MPU401_OK2WR   equ   040h
  32. MPU401_OK2RD   equ   080h
  33.  
  34. ; SB-MIDI definitions
  35.  
  36. MIDI_IN_P      equ   30h                  ; MIDI read (polling mode)
  37. MIDI_IN_I      equ   31h                  ; MIDI read (interrupt mode)
  38. MIDI_UART_P    equ   34h                  ; MIDI UART mode (polling mode)
  39. MIDI_UART_I    equ   35h                  ; MIDI UART mode (interrupt mode)
  40. MIDI_UART_TS_P equ   36h                  ; same as 0x34 with timestamp
  41. MIDI_UART_TS_I equ   37h                  ; same as 0x35 with timestamp
  42. MIDI_OUT_P     equ   38h                  ; MIDI write (polling mode)
  43.  
  44. ; SB Port definitions
  45.  
  46. DSP_RST        equ   06h                  ; DSP reset port
  47. DSP_RD_ST      equ   0eh                  ; DSP read buffer status port
  48. DSP_RD         equ   0ah                  ; DSP read port
  49. DSP_WR_ST      equ   0ch                  ; DSP write buffer status port
  50. DSP_WR         equ   0ch                  ; DSP write port
  51. DSP_OK2WR      equ   80h
  52. DSP_OK2RD      equ   80h
  53.  
  54.  
  55. ON             equ   1
  56. OFF            equ   0
  57. ERROR           equ   -1
  58. OK           equ   0
  59.  
  60.            .MODEL small, C
  61.            INCLUDE SBCUTILS.INC
  62.  
  63.            PUBLIC Write_MPU401_Cmd
  64.            PUBLIC Write_MPU401_Data
  65.            PUBLIC Write_SBMIDI_Data
  66.            PUBLIC Read_MPU401_Data
  67.            PUBLIC Read_SBMIDI_Data
  68.            PUBLIC Send_MIDI_Message
  69.            PUBLIC Set_MIDI_Vol
  70.            PUBLIC Set_MIDI_Notes_Off
  71.            PUBLIC Set_MIDI_Pitch_Bend
  72.            PUBLIC Reset_MPU401
  73.            PUBLIC Set_UART_Mode
  74.            PUBLIC Detect_MIDI
  75.            PUBLIC Init_MIDI
  76.            PUBLIC Exit_MIDI
  77.            PUBLIC MIDIType
  78.  
  79. ;-----------------------------------------------------------------------------
  80. ;                   DATA
  81. ;-----------------------------------------------------------------------------
  82.                .DATA
  83. MIDIType      db    MPU401         ; MIDI type to use flag
  84. msg            db    3 dup(?)
  85.  
  86. ;-----------------------------------------------------------------------------
  87. ;                                  CODE
  88. ;-----------------------------------------------------------------------------
  89.                .CODE
  90. ;------------------------------------------------------------------------
  91. ;  WRITE_MPU401_CMD: sends a command to the MPU401 port.
  92. ;
  93. ;  Input: command - command byte
  94. ;
  95. ;  Output: none
  96. ;------------------------------------------------------------------------
  97. Write_MPU401_Cmd    PROC command:BYTE
  98.  
  99.            push ax
  100.                push dx
  101.  
  102.            mov dx, blastInfo.midiPort ; MPU-401 base I/O address
  103.                inc dx                     ; status port
  104. Busy1:
  105.                in al, dx                  ; read status port
  106.                test al, MPU401_OK2WR      ; ready for output?
  107.                jnz Busy1                  ; nope
  108.  
  109.            mov al, command          ; get command byte
  110.                out dx, al                 ; write command byte
  111.  
  112.                pop dx
  113.                pop ax
  114.                ret
  115.  
  116. Write_MPU401_Cmd    ENDP
  117.  
  118. ;------------------------------------------------------------------------
  119. ;  WRITE_MPU401_DATA: sends a data byte to the MPU401 port.
  120. ;
  121. ;  Input: data - data byte
  122. ;
  123. ;  Output: none
  124. ;------------------------------------------------------------------------
  125. Write_MPU401_Data    PROC data:BYTE
  126.  
  127.                push ax
  128.                push dx
  129.  
  130.            mov dx, blastInfo.midiPort ; MPU-401 base I/O address
  131.                inc dx                     ; status port
  132. Busy2:
  133.                in al, dx                  ; read status port
  134.                test al, MPU401_OK2WR      ; ready for output?
  135.                jnz Busy2                  ; nope
  136.  
  137.            mov al, data          ; get data byte
  138.            dec dx              ; data port
  139.            out dx, al          ; write data byte
  140.  
  141.                pop dx
  142.                pop ax
  143.                ret
  144.  
  145. Write_MPU401_Data    ENDP
  146.  
  147. ;------------------------------------------------------------------------
  148. ;  WRITE_SBMIDI_DATA: sends a byte to the Sound Blaster MIDI port.
  149. ;
  150. ;  Input: data - data byte
  151. ;
  152. ;  Output: none
  153. ;------------------------------------------------------------------------
  154. Write_SBMIDI_Data    PROC data:BYTE
  155.  
  156.            push ax
  157.                push dx
  158.  
  159.            mov dx, blastInfo.baseAddr ; SB base I/O address
  160.                add dl, DSP_WR_ST          ; status port / data port
  161. Busy3:
  162.                in al, dx                  ; read status port
  163.                test al, DSP_OK2WR         ; ready for output?
  164.                jnz Busy3                  ; nope
  165.  
  166.                mov al, MIDI_OUT_P         ; "MIDI out" command
  167.                out dx, al                 ; send "MIDI out" command
  168. Busy4:
  169.                in al, dx                  ; read status port
  170.                test al, DSP_OK2WR         ; ready for output?
  171.                jnz Busy4                  ; nope
  172.  
  173.            mov al, data          ; get data byte
  174.                out dx, al                 ; write data byte
  175.  
  176.                pop dx
  177.                pop ax
  178.                ret
  179.  
  180. Write_SBMIDI_Data    ENDP
  181.  
  182. ;------------------------------------------------------------------------
  183. ;  READ_MPU401_DATA: reads a byte from the MPU401 port.
  184. ;
  185. ;  Input: none
  186. ;
  187. ;  Output: AL - data byte
  188. ;------------------------------------------------------------------------
  189. Read_MPU401_Data    PROC
  190.  
  191.            push dx
  192.  
  193.            mov dx, blastInfo.midiPort ; MPU-401 base I/O address
  194.                inc dx                     ; status port
  195. Busy5:
  196.                in al, dx                  ; read status port
  197.                test al, MPU401_OK2RD      ; MIDI data available?
  198.                jnz Busy5                  ; nope
  199.  
  200.                dec dx                     ; data port
  201.                in al, dx                  ; read data byte / clear MIDI int.
  202.  
  203.                pop dx
  204.                ret
  205.  
  206. Read_MPU401_Data    ENDP
  207.  
  208. ;------------------------------------------------------------------------
  209. ;  READ_SBMIDI_DATA: reads a byte from the Sound Blaster MIDI port.
  210. ;
  211. ;  Input: none
  212. ;
  213. ;  Output: AL - data byte
  214. ;------------------------------------------------------------------------
  215. Read_SBMIDI_Data    PROC
  216.  
  217.                push dx
  218.  
  219.            mov dx, blastInfo.baseAddr ; SB base I/O address
  220.                add dl, DSP_WR_ST          ; status port / data port
  221. Busy6:
  222.                in al, dx                  ; read status port
  223.                test al, DSP_OK2WR         ; ready for output?
  224.                jnz Busy6                  ; nope
  225.  
  226.                mov al, MIDI_IN_P          ; "MIDI in" command
  227.                out dx, al                 ; send "MIDI in" command
  228.  
  229.            mov dx, blastInfo.baseAddr ; SB base I/O address
  230.                add dl, DSP_RD_ST          ; status port
  231. Busy7:
  232.                in al, dx                  ; read status port
  233.                test al, DSP_OK2RD         ; MIDI data available?
  234.                jz Busy7                   ; nope
  235.  
  236.            mov dx, blastInfo.baseAddr ; SB base I/O address
  237.                add dl, DSP_RD             ; data port
  238.                in al, dx                  ; read data byte
  239.                mov ah, al                 ; save it
  240.  
  241.                mov dx, blastInfo.baseAddr ; SB base I/O address
  242.                add dl, DSP_WR_ST          ; status port / data port
  243. Busy8:
  244.                in al, dx                  ; read status port
  245.                test al, DSP_OK2WR         ; ready for output?
  246.                jnz Busy8                  ; nope
  247.  
  248.                mov al, MIDI_IN_P          ; "MIDI in" command
  249.                out dx, al                 ; turn off "MIDI in" command
  250.  
  251.                mov al, ah                 ; retrieve data byte read
  252.  
  253.                pop dx
  254.                ret
  255.  
  256. Read_SBMIDI_Data    ENDP
  257.  
  258. ;------------------------------------------------------------------------
  259. ;  SEND_MIDI_MESSAGE: Sends a MIDI message to port
  260. ;
  261. ;   Input: message - MIDI message to send
  262. ;       size - length of message
  263. ;
  264. ;  Output: none
  265. ;------------------------------------------------------------------------
  266. Send_MIDI_Message    PROC message:FAR PTR BYTE, msgSize:WORD
  267.  
  268.            push cx
  269.            push si
  270.            push es
  271.  
  272.                mov cx, msgSize
  273.            mov si, SEG message
  274.            mov es, si
  275.            mov si, OFFSET message
  276.  
  277.            cld              ; count up
  278.  
  279.            cmp MIDIType, MPU401      ; are we using MPU-401?
  280.                jne WrSBMIDI               ; nope
  281.  
  282. WrMPU401:      push es:[si]               ; get byte
  283.            call Write_MPU401_Data      ; send it via MPU-401
  284.            add sp, 2          ; cleanup stack
  285.            inc si              ; next byte in message
  286.                loop WrMPU401              ; do it again
  287.                jmp short ExitSend
  288. WrSBMIDI:
  289.            push es:[si]          ; get byte
  290.            call Write_SBMIDI_Data      ; send it via SBMIDI
  291.            add sp, 2          ; cleanup stack
  292.            inc si              ; next byte in message
  293.                loop WrSBMIDI              ; do it again
  294. ExitSend:
  295.            pop es
  296.            pop si
  297.            pop cx
  298.                ret
  299.  
  300. Send_MIDI_Message    ENDP
  301.  
  302. ;------------------------------------------------------------------------
  303. ;  SET_MIDI_VOL: Sets the global volume on the requested channel
  304. ;
  305. ;  Input: channel - set volume on this channel (1 to 16)
  306. ;      volume - set channel to this volume (0 to 127)
  307. ;
  308. ;  Output: none
  309. ;
  310. ;  note: if inputs are out of range then no action occurs
  311. ;------------------------------------------------------------------------
  312. Set_MIDI_Vol    PROC channel:BYTE, volume:BYTE
  313.  
  314.            push ax
  315.  
  316.            cmp channel, 16          ; test channel number for
  317.                ja BadVal1                 ;  proper range
  318.            cmp channel, 0
  319.                jbe BadVal1
  320.            cmp volume, 127          ; test volume value for
  321.                ja BadVal1                 ;  proper range
  322.            cmp volume, 0
  323.                jb BadVal1
  324.  
  325.            mov al, channel          ; create MIDI message
  326.            mov [msg], al
  327.            add [msg], 0b0h
  328.            dec [msg]
  329.            mov [msg + 1], 7
  330.            mov al, volume
  331.            mov [msg + 2], al
  332.  
  333.            push 3              ; get message size
  334.            push SEG msg          ; get MIDI message pointer
  335.            push OFFSET msg
  336.            call Send_MIDI_Message
  337.            add sp, 6          ; cleanup stack
  338. BadVal1:
  339.            pop ax
  340.                ret
  341.  
  342. Set_MIDI_Vol    ENDP
  343.  
  344. ;------------------------------------------------------------------------
  345. ;  SET_MIDI_NOTES_OFF: Turns off all notes on the given channel
  346. ;
  347. ;  Input: channel - turns off all notes on this channel (1 to 16)
  348. ;
  349. ;  Output: none
  350. ;
  351. ;  note: if channel is out of range then no action occurs
  352. ;------------------------------------------------------------------------
  353. Set_MIDI_Notes_Off    PROC channel:BYTE
  354.  
  355.            push ax
  356.  
  357.            cmp channel, 16          ; test channel number for
  358.                ja BadVal2                 ;  proper range
  359.            cmp channel, 0
  360.                jbe BadVal2
  361.  
  362.            mov al, channel          ; create MIDI message
  363.            mov [msg], al
  364.            add [msg], 0b0h
  365.            dec [msg]
  366.            mov [msg + 1], 7bh
  367.            mov [msg + 2], 0
  368.  
  369.            push 3              ; get message size
  370.            push SEG msg          ; get MIDI message pointer
  371.            push OFFSET msg
  372.            call Send_MIDI_Message
  373.            add sp, 6          ; cleanup stack
  374. BadVal2:
  375.            pop ax
  376.                ret
  377.  
  378. Set_MIDI_Notes_Off    ENDP
  379.  
  380. ;------------------------------------------------------------------------
  381. ;  SET_MIDI_PITCH_BEND: Sets pitch bend (0 = OFF).
  382. ;
  383. ;  Input: channel - sets pitch bend on this channel (1 to 16)
  384. ;      bend - pitch bend value (-8192 to 8191)
  385. ;  Input: AH - sets pitch bend on this channel (1 to 16)
  386. ;         BX - pitch bend value (-8192 to 8191)
  387. ;
  388. ;  Output: none
  389. ;
  390. ;  note: if inputs are out of range then no action occurs
  391. ;------------------------------------------------------------------------
  392. Set_MIDI_Pitch_Bend    PROC channel:BYTE, bend:WORD
  393.  
  394.            push ax
  395.  
  396.            cmp channel, 16          ; test channel number for
  397.                ja BadVal3                 ;  proper range
  398.            cmp channel, 0
  399.                jbe BadVal3
  400.            cmp bend, 8191          ; test volume value for
  401.                jg BadVal3                 ;  proper range
  402.            cmp bend, -8192
  403.                js BadVal3
  404.  
  405.            mov al, channel          ; create MIDI message
  406.            mov [msg], al
  407.            add [msg], 0e0h
  408.            dec [msg]
  409.            mov ax, bend          ; get bend value
  410.            add ax, 8192          ; make it two 7 bit values
  411.            shl ax, 1
  412.            shr al, 1
  413.            mov WORD PTR [msg + 1], ax
  414.  
  415.            push 3              ; get message size
  416.            push SEG msg          ; get MIDI message pointer
  417.            push OFFSET msg
  418.            call Send_MIDI_Message
  419.            add sp, 6          ; cleanup stack
  420. BadVal3:
  421.            pop ax
  422.                ret
  423.  
  424. Set_MIDI_Pitch_Bend    ENDP
  425.  
  426. ;------------------------------------------------------------------------
  427. ;  RESET_MPU401: resets the MPU401 port.
  428. ;
  429. ;  Input: none
  430. ;
  431. ;  Output: AX - (0 = OK, -1 = ERROR)
  432. ;
  433. ;  note: Error will occur if UART mode is not turned off first.
  434. ;------------------------------------------------------------------------
  435. Reset_MPU401    PROC
  436.  
  437.            push cx
  438.  
  439.            push MPU401_RESET      ; Reset command
  440.            call Write_MPU401_Cmd      ; send it via MPU-401
  441.            add sp, 2          ; cleanup stack
  442.  
  443.                sub cx, cx                 ; Maximum of 65536 tries
  444. Empty1:
  445.            call Read_MPU401_Data
  446.                cmp al, MPU401_CMDOK       ; successful reset?
  447.                je ResetOK                 ; success!
  448.                loop Empty1                 ; try again
  449.  
  450.            mov ax, ERROR
  451.                jmp short ExitReset
  452. ResetOK:
  453.            mov ax, OK
  454. ExitReset:
  455.                pop cx
  456.                ret
  457.  
  458. Reset_MPU401    ENDP
  459.  
  460. ;------------------------------------------------------------------------
  461. ;  SET_UART_MODE: puts the MPU401 port in UART mode.
  462. ;
  463. ;  Input: state - ON or OFF (Reset will return error if UART mode is on
  464. ;                 when reset occurs)
  465. ;
  466. ;  Output: AX - (0 = OK, -1 = ERROR)
  467. ;------------------------------------------------------------------------
  468. Set_UART_Mode     PROC state:WORD
  469.  
  470.                push cx
  471.  
  472.            cmp state, OFF          ; turn UART mode off?
  473.                je TurnOff                 ; yes
  474.  
  475.            push MPU401_UART       ; UART mode command
  476.            call Write_MPU401_Cmd      ; send it via MPU-401
  477.            add sp, 2          ; cleanup stack
  478.  
  479.                sub cx, cx                 ; Maximum of 65536 tries
  480. Empty2:
  481.            call Read_MPU401_Data
  482.            cmp al, MPU401_CMDOK      ; successful mode change?
  483.                je UARTModeOK              ; success!
  484.                loop Empty2                ; try again
  485.  
  486.            mov ax, ERROR
  487.                jmp short ExitSetUART
  488. TurnOff:
  489.                push MPU401_RESET          ; turn off UART mode with reset
  490.                call Write_MPU401_Cmd      ; (will not return acknowledge)
  491.            add sp, 2          ; cleanup stack
  492. UARTModeOK:
  493.            mov ax, OK
  494. ExitSetUART:
  495.                pop cx
  496.                ret
  497.  
  498. Set_UART_Mode     ENDP
  499.  
  500. ;------------------------------------------------------------------------
  501. ;  DETECT_MIDI: Detects the presence of the MPU-401 port or the SBMIDI
  502. ;        port.
  503. ;
  504. ;  Input: none
  505. ;
  506. ;  Output: AX - (0 = OK, -1 = ERROR)
  507. ;------------------------------------------------------------------------
  508. Detect_MIDI    PROC
  509.  
  510.                push dx
  511.  
  512.            cmp MIDIType, MPU401      ; are we using MPU-401?
  513.                jne DtSBMIDI               ; nope
  514.  
  515.            mov dx, blastInfo.midiPort ; MPU-401 base I/O address
  516.                inc dx                     ; status port
  517.                in al, dx                  ; read status port
  518.                test al, MPU401_OK2WR      ; ready for output?
  519.                jnz DtError                ; nope
  520.  
  521.            push OFF           ; turn off UART mode just in case
  522.            call Set_UART_Mode
  523.            add sp, 2          ; cleanup stack
  524.  
  525.            call Reset_MPU401      ; reset the MPU-401 port
  526.            or ax, ax          ; was reset OK?
  527.                jnz DtError                ; nope
  528.  
  529.            mov ax, OK
  530.                jmp short DtExit
  531. DtSBMIDI:
  532.            call DSPReset          ; try to reset DSP
  533.                jmp short DtExit
  534. DtError:
  535.            mov ax, ERROR
  536. DtExit:
  537.                pop dx
  538.                ret
  539.  
  540. Detect_MIDI    ENDP
  541.  
  542. ;------------------------------------------------------------------------
  543. ;  INIT_MIDI: Initializes the MIDI port.  If MPU-401 port then reset
  544. ;          the port and select UART mode.  For either MPU-401 or
  545. ;          SBMIDI ports set all master volumes to 100, turn off all
  546. ;          notes, and reset all pitch bends.
  547. ;
  548. ;  Input: none
  549. ;
  550. ;  Output: AX - (0 = OK, -1 = ERROR)
  551. ;------------------------------------------------------------------------
  552. Init_MIDI    PROC
  553.                push cx
  554.  
  555.            cmp MIDIType, MPU401      ; are we using MPU-401?
  556.                jne ItSBMIDI               ; nope
  557.  
  558.            call Reset_MPU401      ; reset MPU401 port
  559.            or ax, ax          ; was reset OK?
  560.                jnz ItError                ; nope
  561.  
  562.            push ON              ; turn on UART mode
  563.            call Set_UART_Mode
  564.            add sp, 2          ; cleanup stack
  565.            or ax, ax          ; was mode change OK?
  566.                jnz ItError                ; nope
  567. ItSBMIDI:
  568.                mov cx, 16                 ; for sixteen channels
  569. ItNextChl:
  570.            push 100           ; set volume to 100 (GM default)
  571.            push cx
  572.            call Set_MIDI_Vol
  573.  
  574.                call Set_MIDI_Notes_Off    ; turn off all notes
  575.            add sp, 4          ; cleanup stack
  576.            push 0              ; clear all pitch bends
  577.            push cx
  578.            call Set_MIDI_Pitch_Bend
  579.            add sp, 4          ; cleanup stack
  580.                loop ItNextChl             ; do next channel
  581.  
  582.            mov ax, OK
  583.                jmp short ItExit
  584. ItError:
  585.            mov ax, ERROR
  586. ItExit:
  587.                pop cx
  588.                ret
  589.  
  590. Init_MIDI    ENDP
  591.  
  592. ;------------------------------------------------------------------------
  593. ;  EXIT_MIDI: Terminates MIDI driver by turning off all notes (and
  594. ;          turning off UART mode if using MPU-401 port).
  595. ;
  596. ;  Input: none
  597. ;
  598. ;  Output: none
  599. ;------------------------------------------------------------------------
  600. Exit_MIDI    PROC
  601.                push cx
  602.  
  603.                mov cx, 16                 ; for sixteen channels
  604. ExNextChl:
  605.            push cx
  606.            call Set_MIDI_Notes_Off      ; turn off all notes
  607.            add sp, 2          ; cleanup stack
  608.                loop ExNextChl             ; do next channel
  609.  
  610.            cmp MIDIType, MPU401      ; are we using MPU-401?
  611.                jne ExExit                 ; nope
  612.  
  613.            push OFF           ; turn off UART mode
  614.            call Set_UART_Mode
  615.            add sp, 2          ; cleanup stack
  616. ExExit:
  617.                pop cx
  618.                ret
  619.  
  620. Exit_MIDI    ENDP
  621.  
  622.              END
  623.  
  624.